package com.ibm.cloud.banking.account.service;


import com.ibm.cloud.banking.account.component.AccountBasisComponent;
import com.ibm.cloud.banking.account.entity.AccountInfo;
import com.ibm.cloud.banking.account.entity.AccountTransDetail;
import com.ibm.cloud.banking.constant.TransErrorMgt;
import com.ibm.cloud.banking.facade.account.IAccountFacade;
import com.ibm.cloud.banking.facade.account.dto.*;
import com.ibm.cloud.banking.facade.customer.dto.CustAcctRelDTO;
import com.ibm.cloud.banking.generator.SleuthIdGenerator;
import com.ibm.cloud.banking.platform.exception.BusinessException;
import com.ibm.cloud.banking.utils.ValueCopyUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

import io.micrometer.core.instrument.util.StringUtils;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * provides Global Transaction integration support, via account controller
 * @author LONGFAN
 *
 */
@Service
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = RuntimeException.class)
public class AccountDomainService  implements IAccountFacade {


	@Autowired
	private AccountBasisComponent accountBasisComponent;;

	@Autowired
	private SleuthIdGenerator sleuthIdGenerator;

//	@Autowired
//	private SnowFlakeIdGenerator snowFlakeIdGenerator;

	private static final String PREFIX_COMPENSATION = "_compensation";
	Logger logger = LoggerFactory.getLogger(getClass());

//	@Bean(TRANSFER_IN + PREFIX_COMPENSATION)
//	ICompensatable transferInRefund() {
//		return (args) -> {
//			AccountDTO dto = (AccountDTO) args[0];
//			accountComponent.transferInRefund(dto);
//			try {
//				// ...do other transactions
//			} catch (Exception e) {
//				if (accountComponent.isHotspotResource(dto.getAccountNo())) {
//					accountComponent.cancel(TransContext.get().common.getTrace().getTraceId(), dto.getAccountNo(),
//							dto.getBalance().negate());
//				}
//				throw e;
//			}
//			// apply hotspot resource, before commit
//			if (accountComponent.isHotspotResource(dto.getAccountNo())) {
//				accountComponent.confirm(TransContext.get().common.getTrace().getTraceId(), dto.getAccountNo(), dto.getBalance().negate());
//			}
//		};
//	}
//
//	@Bean(TRANSFER_OUT + PREFIX_COMPENSATION)
//	ICompensatable transferOutRefund() {
//		return (args) -> {
//			AccountDTO dto = (AccountDTO) args[0];
//			accountComponent.transferOutRefund(dto);
//			try {
//				// ...do other transactions
//			} catch (Exception e) {
//				if (accountComponent.isHotspotResource(dto.getAccountNo())) {
//					accountComponent.cancel(TransContext.get().common.getTrace().getTraceId(), dto.getAccountNo(), dto.getBalance());
//				}
//				throw e;
//			}
//			// apply hotspot resource, before commit
//			if (accountComponent.isHotspotResource(dto.getAccountNo())) {
//				accountComponent.confirm(TransContext.get().common.getTrace().getTraceId(), dto.getAccountNo(), dto.getBalance());
//			}
//		};
//	}
//
//	@Bean(CREATE_ACCOUNT + PREFIX_COMPENSATION)
//	ICompensatable createAccountRefund() {
//		return (args) -> {
//			//FIXME: refund by using different request parameters with normal method
//			String accountNo = (String) args[0];
//			TransContext.Common common= (TransContext.Common) args[1];
//			accountComponent.createAccountRefund(accountNo,common);
//			//TODO：移除路由数据
//		};
//	}
//
//	
//	@CompensatableTransactional(compensator = TRANSFER_IN + PREFIX_COMPENSATION, lock = "[0].accountNo")
//	public String transferIn(AccountDTO dto) {
//		accountComponent.transferIn(dto);
//		try {
//			// ...do other transactions
//		} catch (Exception e) {
//			if (accountComponent.isHotspotResource(dto.getAccountNo())) {
//				accountComponent.cancel(TransContext.get().common.getTrace().getTraceId(), dto.getAccountNo(), dto.getBalance());
//			}
//			throw e;
//		}
//		// apply hotspot resource, before commit
//		if (accountComponent.isHotspotResource(dto.getAccountNo())) {
//			accountComponent.confirm(TransContext.get().common.getTrace().getTraceId(), dto.getAccountNo(), dto.getBalance());
//		}
//		return dto.getAccountNo();
//	}
//
//	
//	@CompensatableTransactional(compensator = TRANSFER_OUT + PREFIX_COMPENSATION, lock = "[0].accountNo")
//	public String transferOut(AccountDTO dto) {
//		accountComponent.transferOut(dto);
//		try {
//			// ...do other transactions
//		} catch (Exception e) {
//			if (accountComponent.isHotspotResource(dto.getAccountNo())) {
//				accountComponent.cancel(TransContext.get().common.getTrace().getTraceId(), dto.getAccountNo(), dto.getBalance().negate());
//			}
//			throw e;
//		}
//		// apply hotspot resource, before commit
//		if (accountComponent.isHotspotResource(dto.getAccountNo())) {
//			accountComponent.confirm(TransContext.get().common.getTrace().getTraceId(), dto.getAccountNo(), dto.getBalance().negate());
//		}
//		return dto.getAccountNo();
//	}

//	
//	@CompensatableTransactional(compensator = CREATE_ACCOUNT + PREFIX_COMPENSATION)
//	public AccountDTO createAccount(String custNo,String accountNo,String tellerId, BigDecimal balance) {
////		String resultAccountNo= service.createAccount(custNo, accountNo,tellerId,balance);
//		String resultAccountNo= accountComponent.createByEntity(custNo, accountNo,tellerId,balance);
//
//		//routing map not update yet, create local temporary mapping
////		IRoutingTableService.createLocalMapping(MappingType.AccountNo,resultAccountNo,
////				MappingType.CustomerNo,custNo);
//
//		return accountComponent.findByAccountNo(resultAccountNo);
//	}
//	
//	public List<AccountDTO> findByCustNo(String custNo) {
//		return accountComponent.findByCustNo(custNo);
//	}
//
//	
//	public AccountDTO findByAccountNo(String accountNo) {
//		return accountComponent.findByAccountNo(accountNo);
//	}

//	
//	public int checkAccountStatus(String accountNo) {
//		return accountComponent.checkAccountStatus(accountNo);
//	}

	@Override
	public int getSingleProductEffectiveAcctCnt(List<CustAcctRelDTO> custAcctRels, int sameCustAcctCnt) {

		//客户同一产品有效账号列表
		List<AccountInfo> accountInfos = accountBasisComponent.checkAccountStatus(custAcctRels);

		if(accountInfos!=null && accountInfos.size()>sameCustAcctCnt){
			throw new BusinessException(TransErrorMgt.CUSTOMER_MAX_ACCOUNT_COUNT.code,
					TransErrorMgt.CUSTOMER_MAX_ACCOUNT_COUNT.message);
		}

		return accountInfos==null ? 0 : accountInfos.size();
	}

	@Override
	public int getSingleProductEffectiveAcctCnt(CustAcctRelDTO custAcctRelDTO) {
		return this.getSingleProductEffectiveAcctCnt(custAcctRelDTO.getCustAcctRelList(), custAcctRelDTO.getMaxAcctCount());
	}


	@Override
	public AccountInfoDTO checkAcctWithdrawalStatus(String accountNo, BigDecimal withdrawalAmount) {
		// 如果查询的账号不存在，则抛出异常，结束交易
		AccountInfo accountInfo = accountBasisComponent.getByAccountNo(accountNo);

		// 检查账户信息“账户状态”
		if(!AccountInfo.ACCOUNT_STATUS_NORMAL.equals(accountInfo.getStatus())){
			throw new BusinessException(TransErrorMgt.ACCOUNT_STATUS_NOT_NORMAL.code,
					TransErrorMgt.ACCOUNT_STATUS_NOT_NORMAL.message);
		}

		// 检查账户信息“帐户控制状态”
		//1-只收不付(NOT_ALLOW_PAYMENT)或,3-封存
		if(AccountInfo.ACCOUNT_CONTROL_STATUS_DEPOSIT_NOT_ALLOW_WITHDRAWAL.equals(accountInfo.getControlStatus())
				|| AccountInfo.ACCOUNT_CONTROL_STATUS_SEALED.equals(accountInfo.getControlStatus())){
			throw new BusinessException(TransErrorMgt.ACCOUNT_NOT_ALLOW_PAYMENT.code,
					TransErrorMgt.ACCOUNT_NOT_ALLOW_PAYMENT.message);
		}

		// 转出金额检查
		if (1 == withdrawalAmount.compareTo(accountInfo.getAvailableBalance())) {
			throw new BusinessException(TransErrorMgt.ACCOUNT_BALANCE_INSUFFICIENT.code,
					TransErrorMgt.ACCOUNT_BALANCE_INSUFFICIENT.message);
		}

		logger.info("支取账号：{}，账户状态、账户控制状态、可支取金额检查通过。",accountNo);

		AccountInfoDTO accountInfoDTO = new AccountInfoDTO();
		ValueCopyUtils.beanToBean(accountInfo,accountInfoDTO);
		return accountInfoDTO;
	}

	@Override
	public AccountInfoDTO checkAcctWithdrawalStatus(AccountInfoDTO accountDTO) {
		return this.checkAcctWithdrawalStatus(accountDTO.getAccountNo(),accountDTO.getTransAmount());
	}

	@Override
	public AccountInfoDTO checkAcctDepositStatus(String accountNo, String name) {

		// 如果查询的账号不存在，则抛出异常，结束交易
		AccountInfo accountInfo = accountBasisComponent.getByAccountNo(accountNo);

		// 检查账户信息“账户状态”
		if(!AccountInfo.ACCOUNT_STATUS_NORMAL.equals(accountInfo.getStatus())){
			throw new BusinessException(TransErrorMgt.ACCOUNT_STATUS_NOT_NORMAL.code,
					TransErrorMgt.ACCOUNT_STATUS_NOT_NORMAL.message);
		}

		// 检查账户信息“帐户控制状态”
		// 2-只付不收(NOT_ALLOW_DEPOSIT),3-封存
		if(AccountInfo.ACCOUNT_CONTROL_STATUS_PAYMENT_NOT_ALLOW_DEPOSIT.equals(accountInfo.getControlStatus())
				|| AccountInfo.ACCOUNT_CONTROL_STATUS_SEALED.equals(accountInfo.getControlStatus())){
			throw new BusinessException(TransErrorMgt.ACCOUNT_NOT_ALLOW_DEPOSIT.code,
					TransErrorMgt.ACCOUNT_NOT_ALLOW_DEPOSIT.message);
		}

		if(!name.equals(accountInfo.getName())){
			throw new BusinessException(TransErrorMgt.DEPOSIT_ACCOUNT_NAME_NOT_MATCH.code,
					TransErrorMgt.DEPOSIT_ACCOUNT_NAME_NOT_MATCH.message);
		}

		logger.info("存入账号：{}，账户状态、账户控制状态、账户姓名检查通过。",accountNo);

		AccountInfoDTO accountInfoDTO = new AccountInfoDTO();
		ValueCopyUtils.beanToBean(accountInfo,accountInfoDTO);
		return accountInfoDTO;
	}

	@Override
	public AccountInfoDTO checkAcctDepositStatus(AccountInfoDTO accountDTO) {
		return this.checkAcctDepositStatus(accountDTO.getAccountNo(), accountDTO.getCustomerName());
	}

	@Override
	public List<AcctOpenAndCloseDetailDTO> getOpenAndCloseAccts(String transDate) {
		return null; //return this.accountInfoDomainService.getOpenAndCloseAccts(transDate);
	}


	
//	@CompensatableTransactional(compensator = ACCT_TRANSFER_OUT + PREFIX_COMPENSATION, lock = "[0].accountNo")
//	@CompensatableTransactional(compensator = ACCT_TRANSFER_OUT + PREFIX_COMPENSATION)
	@Override
//	@Transactional
	public String acctTransferOut(AccountBalanceUpdateDTO paramsDto){
		return this.updateAccountBalance(paramsDto);
	}

	
//	@CompensatableTransactional(compensator = ACCT_TRANSFER_IN + PREFIX_COMPENSATION, lock = "[0].accountNo")
//	@CompensatableTransactional(compensator = ACCT_TRANSFER_IN + PREFIX_COMPENSATION)
	@Override
//	@Transactional
	public String acctTransferIn(AccountBalanceUpdateDTO paramsDto) {
		String transNo =  this.updateAccountBalance(paramsDto);
		if(paramsDto.getTransAmount().compareTo(new BigDecimal(0.7))==-1){
			throw new RuntimeException("转账金额必须大于0.7。");
		}
		return transNo;
	}

	@Override
//	@Transactional
	public void acctTransferOutRefund(AccountBalanceUpdateDTO accountBalanceUpdateDTO){
		accountBalanceUpdateDTO.setTransAmount(accountBalanceUpdateDTO.getTransAmount().multiply(new BigDecimal(-1)));
		accountBalanceUpdateDTO.setReverseFlag(AccountTransDetail.TRANSACTION_REVERSE);
		accountBalanceUpdateDTO.setDeposit(true);
		accountBalanceUpdateDTO.setComment("交易异常，主动发起冲正，抵扣账户转出金额");
		accountBalanceUpdateDTO.setReverseDate(new Date());
		this.updateAccountBalanceForRefund(accountBalanceUpdateDTO, true);
	}

	@Override
	public Integer deleteAccount(String accountNo) {
		return accountBasisComponent.deleteAccount(accountNo);
	}

	@Override
	public AccountLimitAmtTxSumDTO checkAcctDayWithdrawLimit(AccountLimitAmtTxSumDTO accountLimitAmtTxSumDTO) {
		// 账户限额配置为空，不限制
		if(accountLimitAmtTxSumDTO.getDayWithdrawLimit()==null){
			return new AccountLimitAmtTxSumDTO();
		}

		AccountLimitAmtTxSumDTO existLimitAmt = accountBasisComponent.getAcctDayWithdrawLimit(accountLimitAmtTxSumDTO);

		Optional.ofNullable(existLimitAmt).ifPresent(accountLimitAmtTxSumDTO1 -> {
			if(existLimitAmt.getLimitAmount()==null)
				existLimitAmt.setLimitAmount(new BigDecimal(0));
			if(existLimitAmt.getLimitAmount().add(accountLimitAmtTxSumDTO.getTransAmt()).
					compareTo(accountLimitAmtTxSumDTO.getDayWithdrawLimit())==1){

				BigDecimal remainedLimitAmt = accountLimitAmtTxSumDTO.getDayWithdrawLimit().
						subtract(existLimitAmt.getLimitAmount());
				throw new BusinessException(TransErrorMgt.DAY_WITHDRAW_LIMIT_AMT.code
						,String.format(TransErrorMgt.DAY_WITHDRAW_LIMIT_AMT.message,
						accountLimitAmtTxSumDTO.getDayWithdrawLimit(),
						remainedLimitAmt));
			}
		});
		return existLimitAmt;
	}

	@Override
	public int updateAcctDayWithdrawLimit(AccountLimitAmtTxSumDTO acctLimitAmitDto) {
		return accountBasisComponent.updateAcctDayWithdrawLimit(acctLimitAmitDto);
	}

//	@Bean(ACCT_TRANSFER_OUT + PREFIX_COMPENSATION)
//	ICompensatable acctTransferOutRefund() {
//		return (args) -> {
//			AccountBalanceUpdateDTO accountBalanceUpdateDTO = (AccountBalanceUpdateDTO)args[0];
//			accountBalanceUpdateDTO.setTransAmount(accountBalanceUpdateDTO.getTransAmount().multiply(new BigDecimal(-1)));
//			accountBalanceUpdateDTO.setReverseFlag(AccountTransDetail.TRANSACTION_REVERSE);
//			accountBalanceUpdateDTO.setDeposit(true);
//			accountBalanceUpdateDTO.setComment("交易异常，主动发起冲正，抵扣账户转出金额");
//			accountBalanceUpdateDTO.setReverseDate(new Date());
//			this.updateAccountBalanceForRefund(accountBalanceUpdateDTO, true);
//		};
//	}

	@Override
//	@Transactional
	public void acctTransferInRefund(AccountBalanceUpdateDTO accountBalanceUpdateDTO){
		accountBalanceUpdateDTO.setTransAmount(accountBalanceUpdateDTO.getTransAmount().multiply(new BigDecimal(-1)));
		accountBalanceUpdateDTO.setReverseFlag(AccountTransDetail.TRANSACTION_REVERSE);
		accountBalanceUpdateDTO.setDeposit(false);
		accountBalanceUpdateDTO.setComment("交易异常，主动发起冲正，抵扣账户转入金额");
		accountBalanceUpdateDTO.setReverseDate(new Date());
		this.updateAccountBalanceForRefund(accountBalanceUpdateDTO, true);
	}

//	@Bean(ACCT_TRANSFER_IN + PREFIX_COMPENSATION)
//	ICompensatable acctTransferInRefund() {
//		return (args) -> {
//			AccountBalanceUpdateDTO accountBalanceUpdateDTO = (AccountBalanceUpdateDTO)args[0];
//			accountBalanceUpdateDTO.setTransAmount(accountBalanceUpdateDTO.getTransAmount().multiply(new BigDecimal(-1)));
//			accountBalanceUpdateDTO.setReverseFlag(AccountTransDetail.TRANSACTION_REVERSE);
//			accountBalanceUpdateDTO.setDeposit(false);
//			accountBalanceUpdateDTO.setComment("交易异常，主动发起冲正，抵扣账户转入金额");
//			accountBalanceUpdateDTO.setReverseDate(new Date());
//			this.updateAccountBalanceForRefund(accountBalanceUpdateDTO, true);
//		};
//	}

	//	@Trace
	
//	@CompensatableTransactional(compensator = UPDATE_ACCOUNT_BALANCE + PREFIX_COMPENSATION)
//	@CompensatableTransactional(compensator = UPDATE_ACCOUNT_BALANCE + PREFIX_COMPENSATION,lock = "[0].accountNo")
	@Override
	public String updateAccountBalanceForRefund(AccountBalanceUpdateDTO paramsDto, Boolean isRefund){

		if(StringUtils.isEmpty(paramsDto.getTransNo())){
			paramsDto.setTransNo(UUID.randomUUID().toString());
		}
		accountBasisComponent.updateAccountBalance(paramsDto,isRefund);

		return paramsDto.getTransNo();
	}


//	@Trace
	
//	@CompensatableTransactional(compensator = UPDATE_ACCOUNT_BALANCE + PREFIX_COMPENSATION)
//	@CompensatableTransactional(compensator = UPDATE_ACCOUNT_BALANCE + PREFIX_COMPENSATION,lock = "[0].accountNo")
	@Override
	public String updateAccountBalance(AccountBalanceUpdateDTO paramsDto){

		if(StringUtils.isEmpty(paramsDto.getTransNo())){
			paramsDto.setTransNo(sleuthIdGenerator.nextId());
		}
		accountBasisComponent.updateAccountBalance(paramsDto);

		return paramsDto.getTransNo();
	}


//	@CompensatableTransactional(compensator = SAVING_ACCOUNT + PREFIX_COMPENSATION)
	@Override
	public AccountInfoDTO savingCustAcct(AccountInfoDTO accountInfoDTO) {


//		accountInfoDTO.setTransNo(sleuthIdGenerator.nextId());
		String accountNo = accountBasisComponent.createAccount(accountInfoDTO);

		AccountInfo accountInfo = accountBasisComponent.getByAccountNo(accountNo);
		ValueCopyUtils.beanToBean(accountInfo, accountInfoDTO);
		accountInfoDTO.setOpenAccountAmount(accountInfo.getInitialAmount());

		logger.info("新开账号：{}保存成功，交易流水号：{}",accountNo,accountInfoDTO.getTransNo());

		return accountInfoDTO;
	}

	@Override
	public AccountBalanceQueryRespDTO getAccountBalance(String accountNo) {
		//
		AccountInfo accountInfo = accountBasisComponent.getByAccountNo(accountNo);

		AccountBalanceQueryRespDTO accountBalanceRespDTO = new AccountBalanceQueryRespDTO();

		accountBalanceRespDTO.setAccountNo(accountInfo.getAccountNo());
		accountBalanceRespDTO.setCustomerName(accountInfo.getName());
		accountBalanceRespDTO.setAvailableBalance(accountInfo.getAvailableBalance());

		return accountBalanceRespDTO;
	}

	
//	public List<AccountDTO> querySmallAccount(int balance) {
//		return accountComponent.selectLessThanBalance(balance);
//	}


	
//	@CompensatableTransactional(compensator = TRANSFER_IN_MULTI + PREFIX_COMPENSATION)
//	public String transferInMulti(MultiAccountDTO accounts) {
//		accountComponent.transferInMulti(accounts);
//		return accounts.getRoutingAccountNo();
//	}
//
//	@Bean(TRANSFER_IN_MULTI + PREFIX_COMPENSATION)
//	ICompensatable transferInMultiRefund() {
//		return (args) -> {
//			MultiAccountDTO dto = (MultiAccountDTO) args[0];
//			accountComponent.transferInMultiRefund(dto.getAccounts());
//		};
//	}

	@Override
	public void checkAcctOutDpDw(AccountInfoDTO accountInfoDTO) {
		this.checkAcctOutDpDw(accountInfoDTO.getAccountNo(),accountInfoDTO.getInitialBranch());
	}

	@Override
	public void checkAcctOutDpDw(String accountNoOut, String branchId) {
		// 如果查询的账号不存在，则抛出异常，结束交易
		AccountInfo accountInfoOut = accountBasisComponent.getByAccountNo(accountNoOut);

		if(!accountInfoOut.getInitialBranch().equals(branchId)) {
			if(!"Y".equals(accountInfoOut.getDwFlag())){
				throw new BusinessException(TransErrorMgt.ACCOUNT_NOT_ALLOW_PAYMENT.code,
						TransErrorMgt.ACCOUNT_NOT_ALLOW_PAYMENT.message);
			}
		}

	}

	@Override
	public void checkAcctInDpDw(String accountNoIn, String branchId) {
		// 如果查询的账号不存在，则抛出异常，结束交易
		AccountInfo accountInfoIn = accountBasisComponent.getByAccountNo(accountNoIn);

		if(!accountInfoIn.getInitialBranch().equals(branchId)) {
			if(!"Y".equals(accountInfoIn.getDpFlag())){
				throw new BusinessException(TransErrorMgt.ACCOUNT_NOT_ALLOW_DEPOSIT.code,
						TransErrorMgt.ACCOUNT_NOT_ALLOW_PAYMENT.message);
			}
		}

	}

	@Override
	public void checkAcctInDpDw(AccountInfoDTO accountInfoDTO) {
		this.checkAcctInDpDw(accountInfoDTO.getAccountNo(), accountInfoDTO.getInitialBranch());
	}
}
